package com.googlecode.animator.controller
{
	import com.googlecode.animator.controller.mediators.SWFLoaderMediator;
	import com.googlecode.animator.controller.mediators.VideoStreamMediator;
	import com.googlecode.utils.ResolveFilePath;
	import com.googlecode.utils.UniqueID;
	import com.googlecode.animator.view.cameraProxies.CameraProxy;
	import com.googlecode.animator.view.cameraProxies.TargetProxy;
	import com.googlecode.animator.view.specialmaterials.TextMaterial;
	import com.googlecode.animator.view.specialobjects.PlaneOutlined;
	import com.googlecode.animator.data.Notifications;
	import com.googlecode.data.PositionObject3D;
	import com.googlecode.data.markers.HideMarkerVO;
	import com.googlecode.data.markers.KeyframeVO;
	import com.googlecode.data.materials.BitmapFileMaterialVO;
	import com.googlecode.data.materials.ColorMaterialVO;
	import com.googlecode.data.materials.MaterialVO;
	import com.googlecode.data.materials.MovieMaterialVO;
	import com.googlecode.data.materials.TextMaterialVO;
	import com.googlecode.data.materials.VideoStreamMaterialVO;
	import com.googlecode.data.objects3d.CameraProxyVO;
	import com.googlecode.data.objects3d.CubeVO;
	import com.googlecode.data.objects3d.IObject3DVO;
	import com.googlecode.data.objects3d.Object3DVO;
	import com.googlecode.data.objects3d.PlaneVO;
	import com.googlecode.data.objects3d.TargetProxyVO;
	
	import org.papervision3d.materials.BitmapFileMaterial;
	import org.papervision3d.materials.ColorMaterial;
	import org.papervision3d.materials.utils.MaterialsList;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Cube;
	import org.papervision3d.objects.primitives.Plane;
	import org.papervision3d.scenes.Scene3D;
	import org.puremvc.as3.multicore.interfaces.INotification;

	/**
	 * Adds a 3D object to the model. 
	 * @author USER
	 * 
	 */
	public class AddObject3DCommand extends BaseCommand
	{
		private var _object3D:IObject3DVO;
		private var _mediatorsList:Array;
		private var _videoStreamList:Array;
		private var _resolve:ResolveFilePath;
		
		/**
		 * Executes command. 
		 * @param notification
		 * 
		 */
		override public function execute(notification:INotification):void
		{
			sendNotification(Notifications.TIMELINE_SET_CURRENT_TIME_INTERVAL, 0);
			
			_object3D = notification.getBody() as IObject3DVO;
			_mediatorsList = new Array();
			_videoStreamList = new Array();
			_resolve = ResolveFilePath.instance;
			
			
			var scene:Scene3D = settings.scene;
			var stub:String;
			var object3D:DisplayObject3D;

			// create objects according to type
			if(_object3D is CameraProxyVO)
			{
				stub = "cameraProxy_";
				
				object3D = new CameraProxy();
			}
			
			if(_object3D is TargetProxyVO)
			{
				stub = "targetProxy_";
				
				object3D = new TargetProxy();
			}
			
			if(_object3D is PlaneVO)
			{
				stub = "plane_";
				
				object3D = handlePlane(_object3D as PlaneVO);
			}
			
			if(_object3D is CubeVO)
			{
				stub = "cube_";
				
				object3D = handleCube(_object3D as CubeVO);
			}
			
			// if the object3D has no name, make unique name
			if((_object3D as PositionObject3D).name == null)
			{
				(_object3D as PositionObject3D).name = getName(stub);
			}
			
			// ensure the object3D in the scene has the same name, for referencing purposes
			object3D.name = (_object3D as Object3DVO).name;
			
			/* object3D.x = (_object3D as Object3DVO).x;
			object3D.y = (_object3D as Object3DVO).y;
			object3D.z = (_object3D as Object3DVO).z; */
			
			// transfer the rotational properties from the VO to the object3D in the scene
			object3D.rotationX = (_object3D as Object3DVO).rotationX;
			object3D.rotationY = (_object3D as Object3DVO).rotationY;
			object3D.rotationZ = (_object3D as Object3DVO).rotationZ;
		
			// deal with the keyframes
			var kFrame:KeyframeVO;

			for each(var kf:KeyframeVO in (_object3D as Object3DVO).keyframes)
			{
				// clone the existing keyframe
				kFrame = kf.clone() as KeyframeVO;
				
				// ensure its object3DName property matches that of its parent object3D
				kFrame.object3DName = (_object3D as Object3DVO).name;
				
				// set the position of the object3D in the scene to the position of the keyframe at time = 0
				if(kFrame.time == 0)
				{
					object3D.x = kFrame.position.x;
					object3D.y = kFrame.position.y;
					object3D.z = kFrame.position.z;
				}
				
				// replace the old keyframe with the updated one
				(_object3D as Object3DVO).keyframes[kFrame.id] = kFrame;
			}
			
			
			// deal with hideMarkers
			
			var hMarker:HideMarkerVO;
			
			for each(var hm:HideMarkerVO in (_object3D as Object3DVO).hideMarkers)
			{
				hMarker = hm.clone() as HideMarkerVO;
				hMarker.object3DName = (_object3D as Object3DVO).name;
				
				if(hMarker.begin == 0)
				{
					object3D.visible = false;
				}
				(_object3D as Object3DVO).hideMarkers[hMarker.id] = hMarker;
				
			}
			// if there were no keyframes in the object3D, as is the case when an object3D is created,
			// ensure the first keyframe at time = 0 exists
			if(kFrame == null)
			{
				kFrame = new KeyframeVO();
				kFrame.id = getName("keyframe_");
				kFrame.object3DName = (_object3D as Object3DVO).name;
				kFrame.isDraggable = false;
				kFrame.position.x = (_object3D as Object3DVO).x;
				kFrame.position.y = (_object3D as Object3DVO).y;
				kFrame.position.z = (_object3D as Object3DVO).z;
				
				object3D.x = kFrame.position.x;
				object3D.y = kFrame.position.y;
				object3D.z = kFrame.position.z;
				
				
				(_object3D as Object3DVO).keyframes[kFrame.id] = kFrame;
				
			}
			
			// the object3D in the scene can now be added. This action will not generate any notifications
			scene.addChild(object3D);
			
			// the VO representing the object3D in the scene can now be added. The only thing it lacks are the correctly
			// calculated tweens. This operation can only be done once the VO has been added
			settings.addObject3D(_object3D as Object3DVO);
			
			// calculate the tweens			
			settings.calculateTweens((_object3D as Object3DVO).name);

			// at this point we can send a notification that the recently added object3D is the selected
			// object. This ensures that all newly added objects3D are automatically selected
			sendNotification(Notifications.OBJECT3D_SELECTED_MODEL, (_object3D as Object3DVO).name);
			
			
			for(i=0; i<_videoStreamList.length; i++)
			{
				facade.registerMediator(_videoStreamList[i]);
			}
			
			for(var i:uint=0; i<_mediatorsList.length; i++)
			{
				facade.registerMediator(_mediatorsList[i]);
			}
			
			
			
			
		}
		
		/**
		 * Generates unique name. 
		 * @param stub
		 * @return 
		 * 
		 */
		protected function getName(stub:String):String
		{
			var uniqueID:UniqueID = new UniqueID();
			
			return stub + uniqueID.generateID();
		}
		
		/**
		 * Returns a plane with visible edges. 
		 * @param VO
		 * @return 
		 * 
		 */
		public function handlePlane(VO:PlaneVO):Plane
		{
			var material:* = getMaterial(VO.material);
			
			var plane:PlaneOutlined = new PlaneOutlined(material, VO.width, VO.height, VO.segmentsH, VO.segmentsW);
			
			return plane;
		}
		
		/**
		 * Returns a cube. 
		 * @param VO
		 * @return 
		 * 
		 */
		public function handleCube(VO:CubeVO):Cube
		{
			var mList:MaterialsList = new MaterialsList();
			
			
			for each(var mat:MaterialVO in VO.materialsList)
			{
				mList.addMaterial(getMaterial(mat), mat.face);
			}  

			var cube:Cube = new Cube(mList, VO.width, VO.depth, VO.depth);
			
			return cube;
		}
		
		/**
		 * Returns the actual material needed for the object3D. 
		 * @param matVO
		 * @return 
		 * 
		 */
		protected function getMaterial(matVO:MaterialVO):*
		{
			var material:*;
			
			if(matVO is BitmapFileMaterialVO)
			{
				trace(_resolve.getPath(matVO as BitmapFileMaterialVO, settings.savePath));
				material = new BitmapFileMaterial(_resolve.getPath(matVO as BitmapFileMaterialVO, settings.savePath));
			}
			
			if(matVO is ColorMaterialVO)
			{
				material = new ColorMaterial();
				material.fillAlpha = (matVO as ColorMaterialVO).fillAlpha;
				material.fillColor = (matVO as ColorMaterialVO).fillColor;
			}
			
			var clone3D:Object3DVO
			
			if(matVO is MovieMaterialVO)
			{
				material = new ColorMaterial();
				
				clone3D = _object3D.clone() as Object3DVO;
						
				_mediatorsList.push(new SWFLoaderMediator(clone3D));
			}
			
			if(matVO is TextMaterialVO)
			{
				material = new TextMaterial((matVO as TextMaterialVO).text);
						
			}
			
			if(matVO is VideoStreamMaterialVO)
			{
				material = new ColorMaterial();
				
				clone3D = _object3D.clone() as Object3DVO;
				
				_videoStreamList.push(new VideoStreamMediator(clone3D));

			}
			
			return material;
		}
		
	}
}